In [13]:
Pkg.add("JuMP")
Pkg.add("Cbc")
;
In [12]:
using JuMP
;
Recipe Type and some macros
In [3]:
type Recipe
name::AbstractString
time::Float64
outs::Dict{AbstractString, Float64}
ins::Dict{AbstractString, Float64}
function Recipe(name, time, speed)
recips[name] = new(name, time/speed, Dict{AbstractString, Float64}(), Dict{AbstractString, Float64}())
recips[name]
end
end
recips = Dict{AbstractString, Recipe}()
macro RIN(iname, iqty)
return quote
recipe.ins[$iname] = $iqty / recipe.time
end
end
macro ROUT(iname, iqty)
return quote
recipe.outs[$iname] = $iqty / recipe.time
end
end
The Assembler / Chemical Plant / Smelter / Drill speeds. We can tweak these or add more to change the speed from inserting modules. The Wiki says an Assembler 3 with crafting speed 1.25 when fully boosted with four level 3 speed modules, has a crafting speed of 3.75 but 1.25 x 1.5 x 1.5 x 1.5 x 1.5 = 6.328125 so clearly there is something I don't understand, so I haven't included that
In [24]:
const ASS3SPD = 1.25
const CHEMSPD = 1.25
const SMELTSPD = 2
const DRILLSPD = 1 / 0.525
;
Specify the crafting recipes, some macros (@RIN & @ROUT) make it more readable
In [5]:
recipe = Recipe("Production 1", 15, ASS3SPD)
@RIN "Red Circuit" 5
@RIN "Green Circuit" 5
@ROUT "Production 1" 1
recipe = Recipe("Production 2", 30, ASS3SPD)
@RIN "Red Circuit" 5
@RIN "Blue Circuit" 5
@RIN "Production 1" 4
@ROUT "Production 2" 1
recipe = Recipe("Production 3", 60, ASS3SPD)
@RIN "Red Circuit" 5
@RIN "Blue Circuit" 5
@RIN "Production 2" 4
@RIN "Alien Artifact" 1
@ROUT "Production 3" 1
recipe = Recipe("Blue Circuit", 15, ASS3SPD)
@RIN "Green Circuit" 20
@RIN "Red Circuit" 2
@RIN "Sulphuric Acid" 0.5
@ROUT "Blue Circuit" 1
recipe = Recipe("Red Circuit", 8, ASS3SPD)
@RIN "Green Circuit" 2
@RIN "Plastic" 2
@RIN "Copper Cable" 4
@ROUT "Red Circuit" 1
recipe = Recipe("Green Circuit", 0.5, ASS3SPD)
@RIN "Iron Plate" 1
@RIN "Copper Cable" 3
@ROUT "Green Circuit" 1
recipe = Recipe("Copper Cable", 0.5, ASS3SPD)
@RIN "Copper Plate" 1
@ROUT "Copper Cable" 2
recipe = Recipe("Plastic", 1, CHEMSPD)
@RIN "Coal" 1
@RIN "Petroleum Gas" 3
@ROUT "Plastic" 2
recipe = Recipe("Iron Plate", 3.5, SMELTSPD)
@RIN "Iron Ore" 1
@ROUT "Iron Plate" 1
recipe = Recipe("Copper Plate", 3.5, SMELTSPD)
@RIN "Copper Ore" 1
@ROUT "Copper Plate" 1
recipe = Recipe("Iron Ore", 1, DRILLSPD)
@RIN "Rock" 1
@ROUT "Iron Ore" 1
recipe = Recipe("Copper Ore", 1, DRILLSPD)
@RIN "Rock" 1
@ROUT "Copper Ore" 1
;
All these are the number of crafting stations, we want an integer number and we want zero or more. We can use the same recipes for finding out items below Prod3 e.g. I want 20 Red Circuits an hour, what do I need ?
In [6]:
m = Model()
@variable(m, PROD1s >= 0, Int)
@variable(m, PROD2s>= 0, Int)
@variable(m, PROD3s >= 0, Int)
@variable(m, BLUECs >= 0, Int)
@variable(m, REDCs >= 0, Int)
@variable(m, GREENCs >= 0, Int)
@variable(m, COPPERCs >= 0, Int)
@variable(m, PLASTICs >= 0, Int)
@variable(m, IRONPs >= 0, Int)
@variable(m, COPPERPs >= 0, Int)
@variable(m, IRONOs >= 0, Int)
@variable(m, COPPEROs >= 0, Int)
;
Specify what does each factory produces
In [7]:
Machine = Dict(
PROD3s=>"Production 3",
PROD2s=>"Production 2",
PROD1s=>"Production 1",
BLUECs=>"Blue Circuit",
REDCs=>"Red Circuit",
GREENCs=>"Green Circuit",
COPPERCs=>"Copper Cable",
PLASTICs=>"Plastic",
IRONPs=>"Iron Plate",
COPPERPs=>"Copper Plate",
IRONOs=>"Iron Ore",
COPPEROs=>"Copper Ore"
)
;
These macros make constraints a bit easier to type
In [8]:
macro OUT(fac)
return quote
$fac * recips[Machine[$fac]].outs[Machine[$fac]]
end
end
macro INS(item)
return quote
sum([OtherM * get(recips[Machine[OtherM]].ins, $item, 0) for OtherM in collect(keys(Machine))])
end
end
Constrain all the outputs of each machine to produce at least as much as the inputs of the next machine
In [ ]:
for (M,Item) in Machine
@constraint(m, @OUT(M) >= @INS Item)
end
That's the components, now to specify our problem
We want at least 1 PROD3 every 1/60th of a minute
In [ ]:
@constraint(m, @OUT(PROD3s) >= 1/60)
And, in this case, our objective is to have as few assembly machines as possible (which is shown in the output)
In [9]:
@objective(m, Min, sum(collect(keys(Machine))))
Out[9]:
That's the code for the problem, so we can now run the solver
In [10]:
solve(m)
Out[10]:
A pretty printer
In [22]:
function production(r::Recipe, nfacs)
if nfacs == 0 return end
@printf "\"%s\": %d production facilities\n" r.name nfacs
@printf "\tProduces\n"
for (k,v) in r.outs
@printf "\t\t%0.2f \"%s\" per min - %0.2f per second\n" 60v * nfacs k v * nfacs
end
@printf "\tConsumes\n"
for (k,v) in r.ins
@printf "\t\t%0.2f \"%s\" per min - %0.2f per second\n" 60v * nfacs k v * nfacs
end
end
;
Print out the production numbers for each factory in the order we want - I should really make it search itself, bus as Scarhoof says "It is what it is"
In [23]:
for m in [IRONOs COPPEROs IRONPs COPPERPs COPPERCs GREENCs BLUECs PLASTICs REDCs PROD1s PROD2s PROD3s]
production(recips[Machine[m]], getvalue(m))
end
# and the output